%define _VIDEO_EXTERN_OR_GLOBAL_ 1 
%include"../include/video.mac" 

[section .text] 
;routine to clear screen through video buffer 
clear_screen_buffer:  
	push ax 
	push cx 
	push di 

	mov ax,0x0000 
	mov cx,0x1fff 
	mov di,0x00 
.loop_set: 
	cmp cx,0x00 
	mov byte[gs:di],al 

	add di,0x02 
	sub cx,0x01 
	jb .done 
.done:  
	sti 
	pop di 
	pop cx 
	pop ax 
	ret  

;scroll screen 
;input:
;	1.offset. Offset in the CGA video buffer. 
;output:none.  

;Used only for near call. 
;Stack frame: 
;|offset | 
;|eip	 | 

;|ebp	| 
scroll_screen:  
	push ebp 
	mov ebp,esp   
	push eax 

	;scroll now 
	mov eax,[ebp+4] ;get offset  
	push eax  
	mov dx,CGA_INDEX_REG 
	mov al,CGA_PAGE_LOW 
	out dx,al  
	pop eax 
	mov dx,CGA_DATA_REG 
	out dx,al ;low byte of position  

	push eax 
	mov dx,CGA_INDEX_REG 
	mov al,CGA_PAGE_HIGH 
	out dx,al 
	pop eax 
	mov dx,CGA_DATA_REG 
	mov al,ah 
	out dx,al ;high byte of position  

	pop eax  
	pop ebp 
	ret 4 



;set cursor position routine  
;through video buffer  
;input: 
;	1.offset. Offset in video buffer 
;output:none. 

;Used only for near call. 
;Stack frame: 
;|offset| 
;|eip	| 

;|ebp	| 
set_cursor_pos: 
	push ebp 
	mov ebp,esp   
	push eax 
	push edx 

	;low 8 bits cursor position 
	mov dx,CGA_INDEX_REG 
	mov al,CGA_CURSOR_LOW 
	out dx,al;select cursor register  
	mov eax,[ebp+4] ;get offset  
	mov dx,CGA_DATA_REG 
	out dx,al ;low 8 bits  

	;high 8 bits cursor position  
	mov dx,CGA_INDEX_REG 
	mov al,CGA_CURSOR_HIGH 
	out dx,al 
	mov al,ah 
	mov dx,CGA_DATA_REG 
	out dx,al 

	pop edx 
	pop eax 
	pop ebp 
	ret 4  ;clean up stack  

;show string to screen through video buffer 
;input: 
;	1.string_addr. string address; 
;	2.string_length. string length; 
;	3.pos_x. x coordinate; 
;	4.pos_y. y coordinate;  
;output:none. 


;Used only for near call. 
;stack frame 
;|string_address| 
;|string_length | 
;|pos_x			| 
;|pos_y			| 
;|eip			| 

;|ebp			|	 

show_string_with_length:  
	push ebp 
	mov ebp,esp  ;stack frame pointer  
	push eax 
	push ecx 
	push edx 
	push esi 
	push edi 

	mov esi,[ebp+20] ;string address  
	mov ecx,[ebp+16] ;string length 
	mov dh,[ebp+12];position x  
	mov dl,[ebp+8];position y    

	;test string length   
	;<0,return 
	cmp ecx,0x00 
	jle .exit  

	;offset of (x,y) in video buffer  
	;di:2*(y*screen_width+x) 
	mov ax,0x00  
	mov al,dl ;y  
	mul byte[screen_width] ;y*80  
	add al,dh ;y*80+x  
	adc ah,0x00 
	shl ax,1 ;(y*80+x)*2   
	mov di,ax  

	;set initial cursor position  
	push edi 
	call set_cursor_pos ;set cursor position  

.loop_show: 
	;show all?  
	cmp ecx,0  
	jle .done 

	;show character  
	mov al,[si] ;ASCII  
	mov ah,FONT_COLOR_ATTR ;font property  
	mov word[gs:di],ax 

	;update variable 
	inc si 
	add di,0x02  


	;test whether to scroll_screen 
	;mode:80*25 
	;page 0 offset in video buffer:0-0xfff  
	;page 1:0x1000-0x1fff 
	;page 2:0x2000-0x2fff 
	;page 3:0x3000-0x3fff 
	cmp di,0x1000 
	jz .scroll_page 
	cmp di,0x2000 
	jz .scroll_page 
	cmp di,0x3000 
	jz .scroll_page  
	
	jmp .update_cursor 

.scroll_page:  
	push edi 
	call scroll_screen 

.update_cursor: 
	;update cursor 
	push edi 
	call set_cursor_pos 

	dec ecx  ;length--   
	jmp .loop_show 

.done:  
	;save current (x,y)  
	mov ax,di 
	shr ax,1 ;di/2  
	div byte[screen_width] 
	mov byte[current_cursor_x],ah ;remainder 
	mov byte[current_cursor_y],al ;quotient    

.exit: 
	pop edi 
	pop esi 
	pop edx 
	pop ecx  
	pop eax 
	pop ebp 
	
	ret 16 ;clean up stack  


;show string to screen through video buffer 
;another version 
;input: 
;	1.string_addr. string buffer address; 
;	2.pos_x. x coordinate; 
;	3.pos_y. y coordinate.   
;output:none. 


;Used only for near call. 
;stack frame 
;|string_address| 
;|pos_x			| 
;|pos_y			| 
;|eip			| 

;|ebp			|	 

show_string_with_null:   
	push ebp 
	mov ebp,esp  ;stack frame pointer  
	push eax 
	push edx 
	push esi 
	push edi 

	mov esi,[ebp+16] ;string address  
	mov dh,[ebp+12];position x  
	mov dl,[ebp+8];position y    

	;null string ? 
	cmp byte[esi],0  
	jz .exit 

	;offset of (x,y) in video buffer  
	;di:2*(y*screen_width+x) 
	mov ax,0x00  
	mov al,dl ;y  
	mul byte[screen_width] ;y*80  
	add al,dh ;y*80+x  
	adc ah,0x00 
	shl ax,1 ;(y*80+x)*2   
	mov di,ax  

	;set initial cursor position  
	push edi 
	call set_cursor_pos ;set cursor position  

.loop_show: 
	;show all?  
	cmp byte[esi],0  
	jz .done 

	;show character  
	mov al,[si] ;ASCII  
	mov ah,FONT_COLOR_ATTR ;font property  
	mov word[gs:di],ax 

	;update variable 
	inc esi 
	add di,0x02  

	;test whether to scroll_screen 
	;mode:80*25 
	;page 0 offset in video buffer:0-0xfff  
	;page 1:0x1000-0x1fff 
	;page 2:0x2000-0x2fff 
	;page 3:0x3000-0x3fff 
	cmp di,0x1000 
	jz .scroll_page 
	cmp di,0x2000 
	jz .scroll_page 
	cmp di,0x3000 
	jz .scroll_page  
	
	jmp .update_cursor 

.scroll_page:  
	push edi 
	call scroll_screen 

.update_cursor: 
	;update cursor 
	push edi 
	call set_cursor_pos 

	inc esi 
	jmp .loop_show 

.done:  
	;save current (x,y)  
	mov ax,di 
	shr ax,1 ;di/2  
	div byte[screen_width] 
	mov byte[current_cursor_x],ah ;remainder 
	mov byte[current_cursor_y],al ;quotient    

.exit: 
	pop edi 
	pop esi 
	pop edx 
	pop eax 
	pop ebp 
	
	ret 12 ;clean up stack  

[section .data]
	screen_width db 80 
	screen_heigh db 25 
	bytes_per_page dw 0x1000 ;bytes per page:80*25  

	current_cursor_x db 0x00 ;current cursor  
	current_cursor_y db 0x00   
